package com.huitone.smspfm.socket.core.net;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.huitone.smspfm.socket.model.AccountInfo;

/**
 * 
* @Title ExtSocketManager.java 
* @Package com.huitone.smspfm.socket.core.net 
* @Description ExtSocket管理器,用于管理所有验证通过的socket套接字,管理器可以设置自动清除超时的socket套接字,并且可以指定同一验证用户的套接字可接受的最大值
* @author ybliang   
* @date 2017年10月10日 下午4:30:11 
* @version V1.0
 */
public class ExtSocketManager {
	
	private static final Logger logger = LogManager.getLogger(ExtSocketManager.class);
	
	private Map<String, List<ExtSocket>> allsockets = new ConcurrentHashMap<String, List<ExtSocket>>();
	
	private Map<String, Integer> persktsize = new ConcurrentHashMap<String, Integer>();
	
	/**
	 * 是否启用最大值限制
	 */
	private boolean limit = false;
	
	/**
	 * 只有limit为true时生效,最大值
	 */
	private int maxSize = 1000;
	
	private long outTime = 30000;
	
	private long excuteInterval = 1000;
	
	private boolean clearOutTimeSocketAutomatic = false; 
	
	private Thread watchDog = null;
	
	public static enum AddResult {MaxSize,MaxPerSize,AddSucc,AddFail,MapNull}
	
	/**
	 * 添加新的socket套接字连接到管理器
	 * @param socket
	 * @return 
	 */
	public synchronized AddResult add(ExtSocket socket) {
		
		AddResult result = AddResult.MapNull;
		
		if (socket != null && socket.getAccountInfo() != null && allsockets != null) {
			
			AccountInfo accountInfo = socket.getAccountInfo();
			
			addPersktsize(accountInfo.getSrvNode(), accountInfo.getMaxConn());
			
			if (limit) {
				
				int currSize = this.size();
				
				if (currSize >= maxSize) {
					
					this.clearTimeOutSockets();
					
					currSize = this.size();
					
					if (currSize >= maxSize) {
						result = AddResult.MaxSize;
					} else {
						return addPerSocketList(socket);
					}
					
				} else {
					return addPerSocketList(socket);
				}
			} else {
				
				String string = socket.getAccountInfo().getSrvNode();
				
				List<ExtSocket> sockets = allsockets.get(string);
				
				if (sockets == null) {
					sockets = new Vector<ExtSocket>();
					allsockets.put(string, sockets);
				}
				result = sockets.add(socket) ? AddResult.AddSucc : AddResult.AddFail;
			}
			
		} else {
			result = AddResult.AddFail;
		}
		
		return result;
	}
	
	private synchronized AddResult addPerSocketList(ExtSocket socket) {
		
		AddResult result = AddResult.AddFail;
		
		String string = socket.getAccountInfo().getSrvNode();
		
		List<ExtSocket> sockets = allsockets.get(string);
		
		if (sockets == null) {
			sockets = new Vector<ExtSocket>();
			allsockets.put(string, sockets);
		}
		
		int currPerSize = sockets.size();
		
		Integer maxPerSize = persktsize.get(string) == null ? 10 : persktsize.get(string);
		
		if (currPerSize < maxPerSize) {
			result = sockets.add(socket) ? AddResult.AddSucc : AddResult.AddFail;
		} else {
			
			boolean addable = false;
			
			Iterator<ExtSocket> iterator = sockets.iterator();
			
			while (iterator.hasNext()) {
				ExtSocket next = iterator.next();
				long lastReadTimeMillis = next.getLastReadTimeMillis();
				long currentTimeMillis = System.currentTimeMillis();
				if (next == null || next.isClosed() || ((currentTimeMillis - lastReadTimeMillis) >= outTime)) {
					iterator.remove();
					addable = true;
				}
			}
			
			result = addable ? sockets.add(socket) ? AddResult.AddSucc : AddResult.AddFail : AddResult.MaxPerSize;
		}
		
		return result;
	}
	
	public synchronized int size() {
		
		int size = 0;
		
		if (allsockets == null) return 0;
		
		Iterator<Entry<String, List<ExtSocket>>> iterator = allsockets.entrySet().iterator();
		
		while(iterator.hasNext()) {
			Entry<String, List<ExtSocket>> entry = iterator.next();
			List<ExtSocket> list = entry.getValue();
			size += list.size();
		}
		
		return size;
	}
	
	public synchronized boolean remove(ExtSocket socket) {
		
		if (allsockets == null) return true; 
		
		String string = socket.getAccountInfo().getSrvNode();
		
		List<ExtSocket> sockets = allsockets.get(string);
		
		if (sockets != null) {
			try {
				socket.close();
			} catch (IOException e) {
				logger.warn("ExtSocketManager.remove(socket)移除socket时,执行socket.close()操作发生异常:" + e.getMessage());
			}
			return sockets.remove(socket);
		} else {
			return true;
		}
	}
	
	/**
	 * options = true 只清除超时的socket, options = false 清除所有socket
	 * @param options
	 */
	private synchronized void clearOpts(boolean options) {
		
		if (allsockets == null) return; 
		
		Iterator<Entry<String, List<ExtSocket>>> iterator = allsockets.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry<String, List<ExtSocket>> entry = iterator.next();
			List<ExtSocket> list = entry.getValue();
			Iterator<ExtSocket> itesockets = list.iterator();
			while (itesockets.hasNext()) {
				ExtSocket socket = itesockets.next();
				if (socket != null) {
					if (socket.isClosed()) {
						itesockets.remove();
					} else {
						
						boolean remove = false;
						
						if (options) {
							long lastReadTimeMillis = socket.getLastReadTimeMillis();
							long currentTimeMillis = System.currentTimeMillis();
							if ((currentTimeMillis - lastReadTimeMillis) >= outTime) {
								remove = true;
							}
						} else {
							remove = true;
						}
						
						if (remove) {
							
							try {
								socket.close();
							} catch (IOException e) {
								logger.warn("ExtSocketManager clear()移除socket时,执行socket.close()操作发生异常:" + e.getMessage());
							}
							
							itesockets.remove();
						}
					}
				} else {
					itesockets.remove();
				}
			}
			if (list.isEmpty()) {
				iterator.remove();
			}
		}
	}
	
	/**
	 * 清除所有socket
	 */
	public synchronized void clearAll() {
		this.clearOpts(false);
	}
	
	/**
	 * 清除超时socket
	 */
	public synchronized void clearTimeOutSockets() {
		this.clearOpts(true);
	}
	
	public synchronized void destory() {
		
		Thread dog = this.getWatchDog();
		
		if (dog != null) {
			dog.interrupt();
		}
		
		this.clearAll();
		
		allsockets = null;
	}

	public Map<String, List<ExtSocket>> getAllsockets() {
		return allsockets;
	}

	public void setAllsockets(Map<String, List<ExtSocket>> allsockets) {
		this.allsockets = allsockets;
	}

	public Map<String, Integer> getPersktsize() {
		return persktsize;
	}

	public void setPersktsize(Map<String, Integer> persktsize) {
		this.persktsize = persktsize;
	}
	
	public void addPersktsize(String srvNode, Integer persktsize) {
		if (this.persktsize != null) {
			this.persktsize.put(srvNode, persktsize);
		}
	}

	public boolean isLimit() {
		return limit;
	}

	/**
	 * 限制Socket管理器能接受的最大Socket套接字连接
	 * @param limit
	 */
	public void setLimit(boolean limit) {
		this.limit = limit;
	}

	public long getOutTime() {
		return outTime;
	}

	public void setOutTime(long outTime) {
		this.outTime = outTime;
	}

	public long getExcuteInterval() {
		return excuteInterval;
	}

	public void setExcuteInterval(long excuteInterval) {
		this.excuteInterval = excuteInterval;
	}

	public Thread getWatchDog() {
		return watchDog;
	}

	public void setWatchDog(Thread watchDog) {
		this.watchDog = watchDog;
	}

	public boolean isClearOutTimeSocketAutomatic() {
		return clearOutTimeSocketAutomatic;
	}

	/**
	 * 是否定时清除超时socket
	 * @param clearOutTimeSocketAutomatic
	 */
	public void setClearOutTimeSocketAutomatic(boolean clearOutTimeSocketAutomatic) {
		
		this.clearOutTimeSocketAutomatic = clearOutTimeSocketAutomatic;
		
		if (this.clearOutTimeSocketAutomatic == true) {
			
			if (watchDog == null || !watchDog.isAlive()) {
				
				watchDog = new Thread() {
					
					private boolean keepGoing = true;

					@Override
					public void run() {
						
						while (!this.isInterrupted() && keepGoing) {
							
							ExtSocketManager.this.clearTimeOutSockets();
							
							try {
								TimeUnit.MILLISECONDS.sleep(excuteInterval);
							} catch (InterruptedException e) {
								keepGoing = false;
							}
						}
					}
				};
				
				watchDog.start();
			}
		} else {
			if (watchDog != null) {
				watchDog.interrupt();
			}
		}
	}

	public int getMaxSize() {
		return maxSize;
	}

	/**
	 * 设置管理器可管理的最大套接字连接数<br>
	 * 注:当且仅当limit参数为true时maxSize参数才有效
	 * @param maxSize
	 */
	public void setMaxSize(int maxSize) {
		this.maxSize = maxSize;
	}

}
